<script setup lang="ts">
import { getMultiFactorParams, getSafetyJudege, GetSafetyJudegeParams, SafetyPoint } from "@/services/task";
import echarts from "common/src/utils/echarts";
import {
  FormRules, ElSelect, ElButton, ElForm, ElFormItem, ElOption,
  ElScrollbar, FormInstance, ElMessage,
  ElRadioGroup, ElRadio
} from "element-plus";
import dayjs from 'dayjs';
import { generateGrayColorMap, generateIntervalColorMap, splitLineColor } from "common/src/utils/special";
import { useTimeRangeStore, useWeatherStore } from "@/store";
import { getColorRamp } from "common/src/utils/color";

interface Props {
  taskId: number;
}

let initCid = '', initVid = '';

const initForm = () => {
  return {
    factor: 1,
    meteo: 1,
    feature: 3,
    probability: 'p80',
    /** 数字越大，等级实际越低 */
    maxLevel: 1,
    /** 数字越大，等级实际越低 */
    minLevel: 2,
    statMethod: 3,
    cid: initCid,
    vid: initVid,
  };
};

const factorOptions = [
  { value: 1, label: '单因子' },
  { value: 2, label: '多因子' },
];

const meteoOptions = [
  { value: 1, label: '风速', unit: 'm/s', maxValue: 80, changeRange: [-0.033, 0.033], thresholdsDefault: [0, 5.5, 8.0, 10.8, 13.9, 17.2, 24.5], },
  { value: 2, label: '有效波高', unit: 'm', maxValue: 30, changeRange: [-0.012, 0.012], thresholdsDefault: [0, 1.25, 2.0, 3.0, 4.0, 5.0, 6.0], },
  { value: 3, label: '波周期', unit: 's', maxValue: 30, changeRange: [-0.025, 0.025], thresholdsDefault: [0, 6, 10], },
  { value: 4, label: '流速', unit: 'm/s', maxValue: 10, changeRange: [-0.0036, 0.0036], thresholdsDefault: [0, 1.0, 1.5, 2.0], },
  { value: 5, label: '降水', unit: 'mm/h', maxValue: 110, changeRange: [-0.0025, 0.0025], thresholdsDefault: [0, 4.0, 8.0, 16.0, 32.0], },
  { value: 6, label: '能见度', unit: 'nm', maxValue: 50, changeRange: [-0.0432, 0.0432], thresholdsDefault: [0, 0.5, 1.0, 2.0, 5.0], isReverse: true, },
];

/** 等级颜色，均值与极值时使用，根据气象阈值数组的长度找对应的颜色数组 */
const levelColorArr = [
  ['#007AFF', '#FFCC02', '#FF0000'],
  ['#007AFF', '#FFCC02', '#FF8002', '#FF0000'],
  ['#007AFF', '#FFCC02', '#FF8002', '#FF0000', '#660099'],
  ['#00CB3E', '#007AFF', '#FFCC02', '#FF8002', '#FF0000', '#660099'],
  ['#90EE90', '#ADD8E6', '#007AFF', '#FFCC02', '#FF8002', '#FF0000', '#660099'],
];

/** 强值频率 0, 20, 40, 60, 80, 100 */
const maxFreqColors = ['#0221B5', '#0086DF', '#51FA73', '#FEF105', '#E7411F', '#981212'].reverse();

/** 气候变化 0, 20, 40, 60, 80, 100 */
const climateChangeColors = [
  '#0202FF', '#0606FF', '#0A0AFF', '#0E0EFF', '#1212FF', '#1616FF', '#1A1AFF', '#1E1EFF',
  '#2222FF', '#2424FF', '#2828FF', '#2C2CFF', '#3030FF', '#3434FF', '#3838FF', '#3C3CFF',
  '#4040FF', '#4444FF', '#4848FF', '#4C4CFF', '#5050FF', '#5454FF', '#5858FF', '#5C5CFF',
  '#6060FF', '#6464FF', '#6868FF', '#6C6CFF', '#7070FF', '#7474FF', '#7878FF', '#7C7CFF',
  '#8080FF', '#8484FF', '#8888FF', '#8C8CFF', '#9090FF', '#9494FF', '#9898FF', '#9C9CFF',
  '#A0A0FF', '#A4A4FF', '#A8A8FF', '#ACACFF', '#B0B0FF', '#B4B4FF', '#B8B8FF', '#BCBCFF',
  '#C0C0FF', '#C4C4FF', '#C8C8FF', '#CCCCFF', '#D0D0FF', '#D4D4FF', '#D8D8FF', '#DCDCFF',
  '#E0E0FF', '#E4E4FF', '#E8E8FF', '#ECECFF', '#F0F0FF', '#F4F4FF', '#F8F8FF', '#FCFCFF',
  '#FFFCFC', '#FFF8F8', '#FFF4F4', '#FFF0F0', '#FFECED', '#FFE8E9', '#FFE4E5', '#FFE0E1',
  '#FFDCDE', '#FFD8DA', '#FFD4D6', '#FFD0D2', '#FFCCCE', '#FFC8CA', '#FFC4C7', '#FFC0C3',
  '#FFBCBF', '#FFB8BB', '#FFB4B7', '#FFB0B3', '#FFACAF', '#FFA8AC', '#FFA4A8', '#FFA0A4',
  '#FF9CA0', '#FF989C', '#FF9498', '#FF9094', '#FF8C90', '#FF888C', '#FF8489', '#FF8085',
  '#FF7C81', '#FF787D', '#FF7479', '#FF7075', '#FF6C71', '#FF686E', '#FF646A', '#FF6066',
  '#FF5C62', '#FF585E', '#FF545A', '#FF5056', '#FF4C52', '#FF484F', '#FF444B', '#FF4047',
  '#FF3C43', '#FF383F', '#FF343B', '#FF3037', '#FF2C34', '#FF2830', '#FF242C', '#FF2028',
  '#FF1C24', '#FF181F', '#FF141B', '#FF1217', '#FF0C13', '#FF080F', '#FF040B', '#FF0007'
].reverse();

/** 多因子 0, 20, 40, 60, 80, 100 */
const multiColors = ['#0221B5', '#0086DF', '#51FA73', '#FEF105', '#E7411F', '#981212'].reverse();

const featureOptions = [
  { value: 1, label: '强值频率' },
  { value: 2, label: '概率极值' },
  { value: 3, label: '平均值' },
  { value: 4, label: '气候变化趋势' },
];

const probabilityOptions = [
  { value: 'p80', label: '五年一遇' },
  { value: 'p90', label: '十年一遇' },
  { value: 'p95', label: '二十年一遇' },
];

const baseLevelOptions = [
  { value: 1, label: 'I级' },
  { value: 2, label: 'II级' },
  { value: 3, label: 'III级' },
  { value: 4, label: 'IV级' },
  { value: 5, label: 'V级' },
  { value: 6, label: 'VI级' },
  { value: 7, label: 'VII级' },
];

const statMethodOptions = [
  { value: 3, label: 'CRITIC法+物理约束法' },
  { value: 1, label: 'CRITIC法' },
  { value: 2, label: '物理约束法' },
];

const props = defineProps<Props>();
const timeRangeStore = useTimeRangeStore();
const weatherStore = useWeatherStore();
const formRef = ref<FormInstance>();
const form = reactive(initForm());
const rules = reactive<FormRules>({
  factor: [
    { required: true, message: '请选择评估类型', trigger: ['blur'] },
  ],
});
const loading = ref(false);
const showChart = ref(false);
const safetyChartDivRef = ref<HTMLDivElement>();
/** 安全评估曲线 */
let safetyChart: echarts.ECharts | null = null;
const cidOptions = ref<typeof probabilityOptions>([]);
const vidOptions = ref<typeof probabilityOptions>([]);
const imgList = ref<{
  /** 秒时间戳 */
  ts: number;
  url: string;
}[]>([]);

const levelOptions = computed(() => {
  const meteoInfo = meteoOptions.find(opt => opt.value === form.meteo)!;
  const { thresholdsDefault, isReverse, unit } = meteoInfo;
  const levelOpts = baseLevelOptions.slice(0, thresholdsDefault.length);
  if (!isReverse) {
    levelOpts.reverse();
  }
  const options = thresholdsDefault.map((thres, idx) => {
    const next = thresholdsDefault[idx + 1] ? `${thresholdsDefault[idx + 1]}${unit}` : 'max';
    const cur = idx === 0 ? 'min' : `${thres}${unit}`;
    const { value, label } = levelOpts[idx];
    return {
      value,
      label: `${label}(${cur}~${next})`,
    };
  });
  // 保持最后选项总是从低到高显示
  if (isReverse) {
    options.reverse();
  }
  return options;
});

/** 更新累积位移曲线 */
const updateSafetyChart = (route: SafetyPoint[], params: {
  factor: number;
  feature: number;
  meteo: number;
}) => {
  const { factor, feature, meteo } = params;
  let name = '';
  // 最大值对应最前面的颜色
  let continuousColors = feature === 1 ? [...maxFreqColors] : feature === 4 ? [...climateChangeColors] : undefined;
  if (factor === 1) {
    name = featureOptions.find(opt => opt.value === feature)?.label || '安全系数';
  } else {
    name = '发生概率';
    continuousColors = [...multiColors];
  }
  const meteoInfo = meteoOptions.find(opt => opt.value === meteo)!;
  const { thresholdsDefault, isReverse, changeRange, unit } = meteoInfo;
  const thresLens = thresholdsDefault.length;
  const colors = [...levelColorArr.find(item => item.length === thresLens)!];
  if (isReverse) {
    colors.reverse();
  }
  let fixedNum = 2;
  let valueUnit = '';
  if (factor === 2) {
    valueUnit = '%';
  } else if ([2, 3].includes(feature)) {
    valueUnit = unit;
  } else {
    valueUnit = '%';
  }
  const visualMapType = ([2, 3].includes(feature) && (factor !== 2)) ? 'piecewise' : 'continuous';
  let continuousMax = 100, continuousMin = 0;
  if (feature === 4) {
    const [min, max] = changeRange;
    continuousMax = max;
    continuousMin = min;
    fixedNum = 3;
  }
  const pieces = thresholdsDefault.map((threshold, idx) => {
    const next = thresholdsDefault[idx + 1];
    const labelIdx = isReverse ? idx : thresLens - idx - 1;
    return {
      color: colors[idx],
      min: idx === 0 ? undefined : threshold,
      max: next,
      label: baseLevelOptions[labelIdx].label,
    };
  });
  if (isReverse) {
    pieces.reverse();
  }
  const visualMapConfig: Record<string, any> = {
    type: visualMapType,
    orient: 'horizontal',
    left: 'center',
    textStyle: {
      color: '#ccc',
    },
  };
  if (visualMapType === 'piecewise') {
    Object.assign(visualMapConfig, {
      pieces,
      itemSymbol: 'circle',
      textGap: 0,
      itemGap: 6,
    });
  } else {
    Object.assign(visualMapConfig, {
      color: continuousColors,
      min: continuousMin,
      max: continuousMax,
      itemWidth: 10,
      textGap: 4,
      text: [continuousMax, continuousMin],
      itemHeight: 280,
      formatter: (value: number) => {
        if (value === 0) {
          return '0';
        } else {
          const fixedValue = value.toFixed(2);
          if (Number(fixedValue) === 0) {
            return value > 0 ? '0' : '-0';
          }
          return fixedValue;
        }
      },
    });
  }
  const data = route.map(point => {
    const xtime = dayjs(point.ts * 1000).format('MM/DD HH:mm');
    const dangerF = point.dangerFactor;
    let dangerFactor = dangerF;
    // 强值频率百分比需要 *100
    if (factor === 1 && feature === 1 && typeof(dangerF) === 'number') {
      dangerFactor = dangerF * 100
    }
    return { xtime, ...point, dangerFactor };
  });
  if (!safetyChart) {
    safetyChart = echarts.init(safetyChartDivRef.value! as HTMLDivElement);
    safetyChart.setOption({
      grid: {
        left: 20,
        right: 30,
        top: 16,
        bottom: 32,
        containLabel: true
      },
      tooltip: {
        trigger: 'axis',
        valueFormatter: (value: number) => {
          if (typeof(value) === 'number' && !Number.isNaN(value)) {
            return `${value?.toFixed(fixedNum)}${valueUnit}`
          } else {
            return '--';
          }
        },
      },
      dataZoom: [{
        type: 'inside',
        start: 0,
        end: 100
      }],
      dataset: {
        dimensions: ['xtime', 'dangerFactor'],
        source: data,
      },
      xAxis: {
        type: 'category',
        nameTextStyle: {
          color: 'rgba(255,255,255,0.45)',
        },
        axisTick: {
          alignWithLabel: true,
        },
        axisLabel: {
          color: 'rgba(255,255,255,0.45)',
        },
      },
      yAxis: [
        {
          type: 'value',
          position: 'left',
          axisLabel: {
            color: 'rgba(255,255,255,0.45)',
          },
          splitLine: {
            lineStyle: {
              color: splitLineColor,
            },
          },
        }
      ],
      series: [
        {
          name: name,
          type: 'line',
          yAxisIndex:  0,
        },
      ],
      visualMap: visualMapConfig,
    });
    safetyChart.on('mousedown', (event: any) => {
      const time = event.data?.ts * 1000;
      if (time !== undefined) {
        timeRangeStore.updateOffset(time - timeRangeStore.start);
      }
    });
  } else {
    safetyChart.setOption({
      tooltip: {
        trigger: 'axis',
        valueFormatter: (value: number) => {
          if (typeof(value) === 'number' && !Number.isNaN(value)) {
            return `${value?.toFixed(fixedNum)}${valueUnit}`
          } else {
            return '--';
          }
        },
      },
      dataset: {
        source: data,
      },
      series: [{ name }],
      visualMap: visualMapConfig,
    });
  }
};

/** 更新区划图层 */
const updateClimateLayerInfo = (params?: {
  factor: number;
  feature: number;
  meteo: number;
}) => {
  const index = imgList.value.findIndex(item => item.ts * 1000 > timeRangeStore.currentTime);
  const imgUrl = imgList.value[index]?.url || imgList.value[0].url;
  let isLevel = false;
  if (params) {
    let colors: (string | number)[][] = [];
    const { factor, feature, meteo } = params;
    if (factor === 1) {
      // 强值频率
      if ([1, 4].includes(feature)) {
        const colorArr = feature === 1 ? [...maxFreqColors] : [...climateChangeColors];
        const step = 1 / (colorArr.length - 1);
        colors = colorArr.reverse().map((color, idx) => {
          return [step * idx, color];
        });
      } else {
        // 概率极值和平均值都适用等级，阶梯画法
        isLevel = true;
        const meteoInfo = meteoOptions.find(opt => opt.value === meteo)!;
        const { thresholdsDefault, isReverse, maxValue } = meteoInfo;
        const thresLens = thresholdsDefault.length;
        const colorArr = [...levelColorArr.find(item => item.length === thresLens)!];
        if (isReverse) {
          colorArr.reverse();
        }
        const step = 1 / thresLens;
        colorArr.forEach((color, idx) => {
          colors.push([step * idx, color]);
          colors.push([step * (idx + 1), color]);
        });
        const arr = generateIntervalColorMap(thresholdsDefault, 5, maxValue, colorArr);
        colors = generateGrayColorMap(arr);
      }
    } else {
      //多因子
      const step = 1 / (multiColors.length - 1);
      colors = [...multiColors].reverse().map((color, idx) => {
        return [step * idx, color];
      });
    }
    const colorRamp = getColorRamp(colors);
    weatherStore.updateClimateLayer(imgUrl, colorRamp, isLevel);
  } else {
    weatherStore.updateClimateLayer(imgUrl);
  }
};

const handleCancelEdit = () => {
  Object.assign(form, initForm());
  showChart.value = false;
  weatherStore.hideClimateLayer();
};

const handleQuery = async () => {
  const valid = await formRef.value?.validate();
  if (!valid) {
    return;
  }
  loading.value = true;
  try {
    const { factor, feature, meteo, minLevel, maxLevel, probability, vid, cid, statMethod } = form;
    const params: GetSafetyJudegeParams = { factor, feature };
    if (factor === 1) {
      Object.assign(params, { meteo, maxLevel: minLevel, minLevel: maxLevel, probability })
    }
    if (factor === 2) {
      Object.assign(params, {
        vid, cid,
        algorithm: statMethod,
      });
    }
    const res = await getSafetyJudege(props.taskId, params);
    showChart.value = true;
    const urls = (res?.urlList || []).map(item => {
      const { url, ts } = item;
      let formatDate = dayjs(ts * 1000).set('h', 0).set('m', 0).set('s', 0).set('ms', 0);
      const day = formatDate.get('date');
      let newDate = 1;
      if (day < 11) {
        newDate = 1;
      } else if (day < 21) {
        newDate = 11;
      } else {
        newDate = 21;
      }
      formatDate = formatDate.set('D', newDate);
      return { url ,ts: Math.floor(formatDate.valueOf() / 1000), label: formatDate.format('YYYY-MM-DD HH:mm:ss') };
    });
    imgList.value = urls;
    updateClimateLayerInfo({factor, feature, meteo});
    setTimeout(() => {
      updateSafetyChart(res.route, {factor, feature, meteo});
    }, 0);
  } catch(err) {
    console.log(err);
    ElMessage({
      type: 'warning',
      message: '查询失败，请重试',
    });
  } finally {
    loading.value = false;
  }
};

const handleMinLevelChange = () => {
  const { minLevel, maxLevel } = form;
  // 数字越大，等级实际越低
  if (maxLevel > minLevel) {
    form.maxLevel = minLevel;
  }
};

const initOptions = async () => {
  const res = await getMultiFactorParams();

  cidOptions.value = (res?.critic || []).map(opt => ({
    label: opt.name,
    value: opt.cid,
  }));
  const unitMap: Record<string, string> = { '能见度': 'nmile', '水深': 'm' };
  vidOptions.value = (res?.vis || []).flatMap((item: any) => {
    const name = item?.name || '';
    const unit = unitMap[name] || '';
    return (item?.critical || []).map((c: any) => ({
      label: `${name}——${c?.value?.start}${unit}～${c?.value?.end}${unit}`,
      value: c?.vid,
    }));
  });
  initCid = cidOptions.value[0]?.value || '';
  initVid = vidOptions.value[0]?.value || '';
  form.cid = initCid;
  form.vid = initVid;
};

watch(() => timeRangeStore.currentTime, (val) => {
  if (weatherStore.climateLayerActive) {
    updateClimateLayerInfo();
  }
}, { immediate: true });

// 要素切换时等级也适配到对应选项
watch(levelOptions, (val) => {
  if (!val.some(opt => opt.value === form.minLevel)) {
    form.minLevel = val[0].value;
  }
  if (!val.some(opt => opt.value === form.maxLevel)) {
    form.maxLevel = val[0].value;
  }
});

onMounted(() => {
  initOptions();
});

defineExpose({
  clear: handleCancelEdit,
});
</script>

<template>
  <ElForm ref="formRef" class="safety-judge-form" :rules="rules" :model="form" label-width="90" label-position="left">
    <ElFormItem prop="factor" label="评估类型">
      <ElRadioGroup v-model="form.factor">
        <ElRadio
          v-for="opt in factorOptions"
          :key="opt.value"
          :value="opt.value"
        >{{ opt.label }}</ElRadio>
      </ElRadioGroup>
    </ElFormItem>
    <!-- 单因子-->
    <template v-if="form.factor === 1">
      <ElFormItem label-width="0">
        <ElFormItem prop="meteo" label="气象要素" :rules="[{ required: true, message: '请选择气象要素', trigger: ['blur'] }]">
          <ElSelect
            v-model="form.meteo"
            popper-class="custom-select-popper"
            placeholder="请选择气象要素"
            style="width: 100px;"
          >
            <ElOption
              v-for="option in meteoOptions"
              :key="option.value"
              :value="option.value"
              :label="option.label"
            />
          </ElSelect>
        </ElFormItem>
        <ElFormItem prop="feature" label="统计特征" :rules="[{ required: true, message: '请选择统计特征', trigger: ['blur'] }]" style="flex: 1;">
          <ElSelect
            v-model="form.feature"
            popper-class="custom-select-popper"
            placeholder="请选择统计特征"
          >
            <ElOption
              v-for="option in featureOptions"
              :key="option.value"
              :value="option.value"
              :label="option.label"
            />
          </ElSelect>
        </ElFormItem>
      </ElFormItem>
      <ElFormItem v-if="form.feature === 2" prop="probability" label="概率极值" required>
        <ElSelect
          v-model="form.probability"
          popper-class="custom-select-popper"
          placeholder="请选择概率极值"
        >
          <ElOption
            v-for="option in probabilityOptions"
            :key="option.value"
            :value="option.value"
            :label="option.label"
          />
        </ElSelect>
      </ElFormItem>
      <ElFormItem v-if="form.feature === 1" label="强值设置" required>
        <ElFormItem label-width="0" style="flex: 1;">
          <ElSelect
            v-model="form.minLevel"
            popper-class="custom-select-popper"
            @change="handleMinLevelChange"
          >
            <ElOption
              v-for="option in levelOptions"
              :key="option.value"
              :value="option.value"
              :label="option.label"
            />
          </ElSelect>
        </ElFormItem>
        <span style="margin: 0 2px; color: #ccc;">~</span>
        <ElFormItem label-width="0px" style="flex: 1;">
          <ElSelect
            v-model="form.maxLevel"
            popper-class="custom-select-popper"
          >
            <ElOption
              v-for="option in levelOptions"
              :key="option.value"
              :disabled="option.value > form.minLevel"
              :value="option.value"
              :label="option.label"
            />
          </ElSelect>
        </ElFormItem>
      </ElFormItem>
    </template>
    <!-- 多因子-->
    <template v-else>
      <ElFormItem prop="statMethod" label="统计方法选择" required label-width="150">
        <ElSelect
          v-model="form.statMethod"
          popper-class="custom-select-popper"
          placeholder="请选择统计方法"
        >
          <ElOption
            v-for="option in statMethodOptions"
            :key="option.value"
            :value="option.value"
            :label="option.label"
          />
        </ElSelect>
      </ElFormItem>
      <ElFormItem v-if="[1, 3].includes(form.statMethod)" prop="cid" label="船舶摇摆度数设置" required label-width="150">
        <ElSelect
          v-model="form.cid"
          popper-class="custom-select-popper"
          placeholder="请选择船舶摇摆度数"
        >
          <ElOption
            v-for="option in cidOptions"
            :key="option.value"
            :value="option.value"
            :label="option.label"
          />
        </ElSelect>
      </ElFormItem>
      <ElFormItem v-if="[2, 3].includes(form.statMethod)" prop="vid" label="物理约束设置" required label-width="150">
        <ElSelect
          v-model="form.vid"
          popper-class="custom-select-popper"
          placeholder="请选择物理约束"
        >
          <ElOption
            v-for="option in vidOptions"
            :key="option.value"
            :value="option.value"
            :label="option.label"
          />
        </ElSelect>
      </ElFormItem>
    </template>
  </ElForm>
  <div class="form-actions dialog-footer">
    <ElButton @click="() => handleCancelEdit()">重置</ElButton>
    <ElButton
      :loading="loading"
      type="primary"
      @click="() => handleQuery()"
    >查询</ElButton>
  </div>
  <div ref="safetyChartDivRef" v-show="showChart" class="task-safety-container"></div>
</template>

<style lang="less" scoped>
.safety-judge-form {
  padding-right: 16px;
  padding-top: 4px;
  padding-bottom: 0;
  :deep(.el-form-item) {
    margin-bottom: 4px;
  }
}
.task-safety-container {
  height: 300px;
}
</style>

<style lang="less">
.custom-dialog.task-info-dialog.el-dialog .dialog-footer.form-actions {
  padding-bottom: 8px;
  padding-right: 16px;
  padding-left: 8px;
  .el-button + .el-button {
    margin-left: 20px;
  }
}
.el-dialog.custom-dialog .el-dialog__body .safety-judge-form .el-form-item__label {
  padding-left: 8px;
  padding-right: 4px;
}
</style>
